﻿Public Class MagicSquare
    Inherits Puzzle

    Public Overrides Sub NewPuzzle()
        MyBase.NewPuzzle()
        Groups.Add(New Group(3, 1, From i In Range(1, 8) Select Find(i, i)))
        Groups.Add(New Group(3, 2, From i In Range(1, 8) Select Find(9 - i, i)))
        For Each Item In Cells
            Dim Cell = Item
            Cell.Groups.Add((From g In Groups Where g.Type = 1 And g.Index = Cell.x).First)
            Cell.Groups.Add((From g In Groups Where g.Type = 2 And g.Index = Cell.y).First)
            If Cell.x = Cell.y Or Cell.x = 9 - Cell.y Then Cell.Groups.Add((From g In Groups Where g.Type = 3 And g.Cells.Contains(Cell)).First)
        Next
    End Sub

    Public Overrides Function OnKeyPress(ByVal Key As String) As Boolean
        Select Case Key
            Case "1" To "8"
                Dim Cell As Cell = (From c As Cell In Cells Where c.x = Cursor.X And c.y = Cursor.Y Select c).First
                Cell.g = Key
                Cell.Invalidate()
                If AutosolveMode Then Solve()
            Case "delete"
                Dim Cell As Cell = (From c As Cell In Cells Where c.x = Cursor.X And c.y = Cursor.Y Select c).First
                Cell.g = " "
                Cell.Invalidate()
                If AutosolveMode Then Solve()
        End Select
        Return MyBase.OnKeyPress(Key)
    End Function

    Public Overrides Sub Load(ByVal Data As String)
        NewPuzzle()
        For i As Integer = 1 To 64
            Dim Cell As Cell = Cells(i - 1)
            Cell.g = Mid(Data, i, 1)
        Next
        Invalidate()
    End Sub

    Public Overrides Function Save() As String
        Dim Text As String = ""
        For i As Integer = 1 To 64
            Dim Cell As Cell = Cells(i - 1)
            Text &= Cell.g
        Next
        Return Text
    End Function

    Public Overrides Sub Solve()
        ClearLog()
        For Each Cell In Cells
            Cell.Reset("12345678")
        Next
        For Each Group In Groups
            Group.Reset()
        Next
        ProcessGiven()
        Dim Level As Integer = 1
        Dim Times(99) As Integer, Counts(99) As Integer
        Dim Watch As New Stopwatch
        Do
            Dim Progress As Boolean = False
            Watch.Reset()
            Watch.Start()
            Select Case Level
                Case 1 : Progress = NakedCandidates()
                Case 2 : Progress = SingleCandidates()
                Case 3 : Progress = NakedPairs()
                Case 4 : Progress = HiddenPairs()
                Case 5 : Progress = NakedTriples()
                Case 6 : Progress = HiddenTriplets()
                Case 7 : Progress = HiddenQuad()
                Case 8 : Progress = XWing()
                Case 9 : Progress = SwordFish()
                Case 10 : Progress = JellyFish()
                Case Else : If Not Progress Then Exit Do
            End Select
            Watch.Stop()
            Counts(Level) += 1
            Times(Level) += Watch.ElapsedMilliseconds
            Level = IIf(Progress, 1, Level + 1)
            FlushLog()
            If (From c As Cell In Cells Where c.f = "" Select c).Count = 0 Then Exit Do
        Loop
        Invalidate()
        Log.Append("")
        For i As Integer = 1 To 99
            If Counts(i) > 0 Then Log.Append(Choose(i, "naked candidates", "single candidates", "candidate lines", "block pairs", "naked pairs", "hidden pairs", "naked triplets", "hidden triplets", "hidden-quad", "x-wing", "swordfish", "jellyfish", "xy-wing", "xyz-wing", "coloring", "multicoloring", "xy-chain") & " : " & Counts(i) & " (" & Times(i) & "ms)")
        Next
        FlushLog()
        Invalidate()
    End Sub

    Sub ProcessGiven()
        For Each Result In From Cell In Cells Where Cell.g <> " " Select Cell
            Result.SetValue(Result.g)
        Next
    End Sub

    Function HiddenQuad() As Boolean
        Dim Progress As Boolean = False
        For Each Result In ( _
                From g In Groups From c1 In g.PossibleCells Where c1.Value.Count.Between(2, 4) _
                From c2 In g.PossibleCells Where c2.Value.Count.Between(2, 4) AndAlso c2.Key > c1.Key Let J12 = c1.Value.Union(c2.Value) Where J12.Count.Between(3, 4) _
                From c3 In g.PossibleCells Where c3.Value.Count.Between(2, 4) AndAlso c3.Key > c2.Key Let J123 = c3.Value.Union(J12) Where J123.Count = 4 _
                From c4 In g.PossibleCells Where c4.Value.Count.Between(2, 4) AndAlso c4.Key > c3.Key Let J1234 = c4.Value.Union(J123) Where J1234.Count = 4 _
                From c In g.PossibleCells Where c.Value.Intersect(J1234).Count > 0 AndAlso Not c.Key.In(c1.Key, c2.Key, c3.Key, c4.Key) Let Cell1 = J1234(0), Cell2 = J1234(1), Cell3 = J1234(2), Cell4 = J1234(3)).ToList
            Log.Append("hidden quad detected at " & Result.Cell1 & Result.Cell2 & Result.Cell3 & Result.Cell4 & " for " & Result.c1.Key & Result.c2.Key & Result.c3.Key & Result.c4.Key & " eliminating other values")
            Result.Cell1.Eliminate(Result.c.Key)
            Result.Cell2.Eliminate(Result.c.Key)
            Result.Cell3.Eliminate(Result.c.Key)
            Result.Cell4.Eliminate(Result.c.Key)
            Progress = True
        Next
    End Function

    Public Sub New()
        Size = 8
    End Sub

End Class
